import 'dart:async';

import 'package:compass/style/theme.dart';
import 'package:flutter/material.dart';
import 'package:geolocator/geolocator.dart';
import 'package:sensors_plus/sensors_plus.dart';
import 'dart:math';

import '../const/render_list.dart';
import '../const/type.dart';
import '../utils/display_utils.dart';
import '../utils/math_utils.dart';
import 'move_and_scale.dart';

class CompassBox extends StatefulWidget {
  const CompassBox(
      {super.key,
      this.onMagneticChanged,
      this.onPositionChanged,
      this.onAccelChanged});

  final void Function({
    required double azimuth,
    required MagneticInfo magnetometerInfo,
  })? onMagneticChanged;

  final void Function({
    required AccelInfo accelInfo,
  })? onAccelChanged;

  final void Function({
    required PositionInfo positionInfo,
  })? onPositionChanged;

  @override
  State<CompassBox> createState() => _CompassBoxState();
}

class _CompassBoxState extends State<CompassBox> {
  StreamSubscription<MagnetometerEvent>? _magneticStream;
  StreamSubscription<AccelerometerEvent>? _accelStream;
  StreamSubscription<Position>? _positionStream;
  bool _isListenMagneticErr = false;
  bool _isListenAccelErr = false;
  bool _isListenPositionErr = false;
  AccelInfo? _accelInfo;
  double _azimuth = 0;
  DateTime _lastInclineTime = DateTime.now();

  final GeolocatorPlatform _geolocatorPlatform = GeolocatorPlatform.instance;

  void _listenMagnetic() {
    _cancelMagneticStream();
    _magneticStream =
        magnetometerEventStream(samplingPeriod: SensorInterval.uiInterval)
            .listen(
      (MagnetometerEvent event) {
        final magneticInfo = MagneticInfo(event.x, event.y, event.z);
        final azimuth = azimuthTransfer(magneticInfo.x, magneticInfo.y);
        setState(() {
          _azimuth = azimuth;
        });
        if (widget.onMagneticChanged != null) {
          widget.onMagneticChanged!(
              azimuth: azimuth, magnetometerInfo: magneticInfo);
        }
      },
      onError: (error) {
        setState(() {
          _isListenMagneticErr = true;
        });
      },
      cancelOnError: true,
    );
  }

  void _listenAccel() {
    _cancelAccelStream();
    _accelStream =
        accelerometerEventStream(samplingPeriod: SensorInterval.uiInterval)
            .listen(
      (AccelerometerEvent event) {
        final accelInfo = AccelInfo(event.x, event.y, event.z);
        setState(() {
          _accelInfo = accelInfo;
        });
        if (widget.onAccelChanged != null) {
          widget.onAccelChanged!(
            accelInfo: accelInfo,
          );
        }
      },
      onError: (error) {
        setState(() {
          _isListenAccelErr = true;
        });
      },
      cancelOnError: true,
    );
  }

  void _listenPositionStream() {
    _cancelPositionStream();
    final positionStream = _geolocatorPlatform.getPositionStream();
    _positionStream = positionStream.handleError((err) {
      _isListenPositionErr = true;
    }).listen((position) {
      if (widget.onPositionChanged != null) {
        widget.onPositionChanged!(
            positionInfo: PositionInfo(
          latitude: position.latitude,
          longitude: position.longitude,
        ));
      }
    });
  }

// 取消订阅磁力传感器信息
  void _cancelMagneticStream() {
    _magneticStream?.cancel();
  }

  // 重力传感器信息
  void _cancelAccelStream() {
    _accelStream?.cancel();
  }

  // 取消订阅位置信息
  void _cancelPositionStream() {
    _positionStream?.cancel();
  }

  @override
  void initState() {
    super.initState();
    _listenMagnetic();
    _listenAccel();
    _listenPositionStream();
  }

  @override
  void deactivate() {
    super.deactivate();
    _cancelMagneticStream();
    _cancelAccelStream();
    _cancelPositionStream();
  }

  @override
  Widget build(BuildContext context) {
    return MoveAndScaleBox(
      child: Container(
          alignment: Alignment.center,
          child:
              _isListenMagneticErr || _isListenAccelErr || _isListenPositionErr
                  ? const Text('手机传感器出现了点小问题')
                  : Transform.rotate(
                      angle: calcRotateForScreen(_azimuth),
                      child: AspectRatio(
                        aspectRatio: 1,
                        child: CustomPaint(
                          painter: DrawingBoard(
                              azimuth: _azimuth,
                              context: context,
                              accelInfo: _accelInfo,
                              lastInclineTime: _lastInclineTime,
                              setLastInclineTime: (DateTime dateTime) {
                                _lastInclineTime = dateTime;
                              }),
                        ),
                      ),
                    )),
    );
  }
}

class DrawingBoard extends CustomPainter {
  BuildContext context;
  double? azimuth = 0;
  double _usedRadius = 0;
  double _drawingBoardR = 0;
  Offset _centerOffset = Offset.zero;
  Canvas? theCanvas;
  AccelInfo? accelInfo;
  DateTime lastInclineTime;
  Function(DateTime dateTime) setLastInclineTime;

  DrawingBoard(
      {this.azimuth,
      required this.context,
      this.accelInfo,
      required this.lastInclineTime,
      required this.setLastInclineTime});

  setUsedRadius(double selfUsedRadius) {
    _usedRadius += selfUsedRadius;
  }

  get startRadius => _usedRadius;

  @override
  void paint(Canvas canvas, Size size) {
    // 初始化
    theCanvas = canvas;
    _drawingBoardR = min(size.width, size.height) / 2;
    _centerOffset = Offset(size.width / 2, size.height / 2);
    _usedRadius = 0;

    // 背景
    drawBackground();

    // 天池
    drawTianChi();

    // 中部文字
    for (int i = 0; i < renderList.length; i++) {
      var element = renderList[i];
      if (!element.show) {
        continue;
      }
      drawCircleTextAndAutoUsedRadius(
          cellSize: element.cellSize,
          fontSize: element.fontSize,
          border: element.border,
          borderColor: compassShadowGray,
          txtList: element.txtList,
          vertical: element.vertical,
          offset: element.offset,
          noneBorderRight: true,
          noneBorderBottom: i > 0);
    }

    // 外层刻度线
    drawScaleMark();

    // 参考线
    drawLineOfReference();
  }

  // 背景
  void drawBackground() {
    Paint paint = Paint()
      ..color = compassGolden
      ..style = PaintingStyle.fill;
    theCanvas?.drawCircle(_centerOffset, _drawingBoardR, paint);
  }

  // 天池
  void drawTianChi() {
    Paint circlePaint = Paint()
      ..color = compassDarkGolden
      ..style = PaintingStyle.fill;

    const double radius = 64;
    setUsedRadius(radius);
    final center = _centerOffset;

    final Paint shadowPaint = Paint()
      ..shader = RadialGradient(
        stops: const [0.9, 1.0],
        colors: <Color>[Colors.transparent, compassShadowGray],
      ).createShader(Rect.fromCircle(center: center, radius: radius));
    theCanvas
      ?..drawCircle(center, radius, circlePaint)
      ..drawCircle(center, radius, shadowPaint);

    // 倾斜圆圈
    drawInclineCircle(radius);

    // 指针
    drawPointer(radius);
  }

  // 倾斜圆圈
  void drawInclineCircle(double radius) {
    Paint paint = Paint()
      ..style = PaintingStyle.stroke
      ..strokeWidth = 2;
    const double ballRadius = 12;
    final dRadius = radius - ballRadius - paint.strokeWidth;

    final x = min((accelInfo?.x ?? 0) / 9.8 * dRadius, dRadius);
    final y = min((accelInfo?.y ?? 0) / 9.8 * dRadius, dRadius);

    bool needDraw = true;
    if (abs(x) > 0.5 || abs(y) > 0.5) {
      paint.color = compassRed;
      setLastInclineTime(DateTime.now());
    } else {
      paint.color = compassDarkGreen;
      // 超出 3 秒隐藏
      if (DateTime.now().difference(lastInclineTime).inSeconds >= 3) {
        needDraw = false;
      }
    }

    if (needDraw) {
      theCanvas
        ?..save()
        ..translate(_centerOffset.dx, _centerOffset.dy)
        ..rotate(calcRotateForScreen(180 - (azimuth ?? 0)))
        ..translate(-x, y)
        ..drawCircle(Offset.zero, ballRadius, paint)
        ..restore();
    }
  }

  // 指针
  void drawPointer(double outerR) {
    Paint pointPaint = Paint()
      ..color = compassDarkRed
      ..style = PaintingStyle.fill;

    Path path = Path()
      ..addRRect(
        RRect.fromRectAndRadius(
            Rect.fromCircle(
              center: _centerOffset,
              radius: 8,
            ),
            const Radius.circular(12)),
      );

    Paint bodyPaint = Paint()
      ..color = compassDarkRed
      ..style = PaintingStyle.stroke
      ..strokeWidth = 3
      ..strokeCap = StrokeCap.round;
    Paint headPaint = Paint()
      ..color = compassDarkRed
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1
      ..strokeCap = StrokeCap.round;

    drawPathAndShadows(path: path, paint: pointPaint, shadows: [
      BoxShadow(
        color: compassShadowDarkGray,
        offset: Offset.zero,
        blurRadius: 3,
        spreadRadius: 0,
      )
    ]);

    theCanvas
      ?..save()
      ..translate(_centerOffset.dx, _centerOffset.dy)
      ..drawLine(Offset(0, -outerR * 0.3), Offset.zero, bodyPaint)
      ..drawLine(Offset.zero, Offset(0, outerR * 0.8), headPaint)
      ..restore();
  }

  // 绘制外阴影
  void drawPathAndShadows(
      {required Path path, required Paint paint, List<BoxShadow>? shadows}) {
    if (shadows != null && shadows.isNotEmpty) {
      for (final BoxShadow shadow in shadows) {
        final Paint shadowPainter = shadow.toPaint();
        if (shadow.spreadRadius == 0) {
          theCanvas?.drawPath(path.shift(shadow.offset), shadowPainter);
        } else {
          Rect zone = path.getBounds();
          double xScale = (zone.width + shadow.spreadRadius) / zone.width;
          double yScale = (zone.height + shadow.spreadRadius) / zone.height;
          Matrix4 m4 = Matrix4.identity();
          m4.translate(zone.width / 2, zone.height / 2);
          m4.scale(xScale, yScale);
          m4.translate(-zone.width / 2, -zone.height / 2);
          theCanvas?.drawPath(
              path.shift(shadow.offset).transform(m4.storage), shadowPainter);
        }
      }
    }
    theCanvas?.drawPath(path, paint);
  }

  // 参考线
  void drawLineOfReference() {
    Paint paint = Paint()
      ..color = compassRed
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1
      ..strokeCap = StrokeCap.round;
    theCanvas
      ?..save()
      ..translate(_centerOffset.dx, _centerOffset.dy)
      ..rotate(calcRotateForScreen(180 - (azimuth ?? 0)))
      ..drawLine(Offset(-_drawingBoardR, 0), Offset(_drawingBoardR, 0), paint)
      ..drawLine(Offset(0, -_drawingBoardR), Offset(0, _drawingBoardR), paint)
      ..restore();
  }

  // 绘制外层刻度线
  void drawScaleMark() {
    Paint paint = Paint()
      ..color = compassBorderDarkGray
      ..style = PaintingStyle.stroke
      ..strokeWidth = 1
      ..strokeCap = StrokeCap.round;

    theCanvas?.drawCircle(_centerOffset, _drawingBoardR, paint);

    final txtDrawR = _drawingBoardR - 10;

    for (int i = 0; i < 360; i++) {
      double len = 4;
      if (i % 10 == 0) {
        len = 8;
        drawText(
            drawR: txtDrawR,
            txt: '$i',
            cellSize: 10,
            fontSize: 10,
            index: (i / 10).ceil(),
            angle: 10);
      } else if (i % 5 == 0) {
        len = 6;
      }
      drawScaleMarkLine(paint: paint, length: len, rotate: i.toDouble());
    }
  }

  // 绘制扇形
  void drawSector({
    required double drawOuterR,
    required double drawInnerR,
    required double angle,
    required int index,
    bool border = false,
    bool noneBorderLeft = false,
    bool noneBorderRight = false,
    bool noneBorderTop = false,
    bool noneBorderBottom = false,
    Color borderColor = compassBorderDarkGray,
    double offset = 0,
    bool fill = false,
    Color? fillColor,
  }) {
    if (!border && !fill) {
      return;
    }

    double radians =
        degreesToRadians(angle) * (index + offset) - degreesToRadians(90);
    final startAngle = -degreesToRadians(angle / 2);
    final endAngle = degreesToRadians(angle / 2);

    Offset pi0 =
        Offset(cos(startAngle) * drawInnerR, -sin(startAngle) * drawInnerR);
    Offset po0 =
        Offset(cos(startAngle) * drawOuterR, -sin(startAngle) * drawOuterR);
    Offset pi1 =
        Offset(cos(endAngle) * drawInnerR, -sin(endAngle) * drawInnerR);
    Offset po1 =
        Offset(cos(endAngle) * drawOuterR, -sin(endAngle) * drawOuterR);

    Path fillPath = Path()
      ..moveTo(pi0.dx, pi0.dy)
      ..lineTo(po0.dx, po0.dy)
      ..arcToPoint(po1, radius: Radius.circular(drawOuterR), clockwise: false)
      ..lineTo(pi1.dx, pi1.dy)
      ..arcToPoint(pi0, radius: Radius.circular(drawInnerR));

    theCanvas
      ?..save()
      ..translate(_centerOffset.dx, _centerOffset.dy)
      ..rotate(radians);

    if (fill) {
      Paint fillPaint = Paint()
        ..style = PaintingStyle.fill
        ..color = fillColor ?? borderColor;
      theCanvas?.drawPath(fillPath, fillPaint);
    }

    if (border) {
      Paint strokePaint = Paint()
        ..style = PaintingStyle.stroke
        ..color = borderColor
        ..strokeWidth = 1;
      Path strokePath = Path()..moveTo(pi0.dx, pi0.dy);
      if (noneBorderLeft) {
        strokePath.moveTo(po0.dx, po0.dy);
      } else {
        strokePath.lineTo(po0.dx, po0.dy);
      }
      if (noneBorderTop) {
        strokePath.moveTo(po1.dx, po1.dy);
      } else {
        strokePath.arcToPoint(po1,
            radius: Radius.circular(drawOuterR), clockwise: false);
      }
      if (noneBorderRight) {
        strokePath.moveTo(pi1.dx, pi1.dy);
      } else {
        strokePath.lineTo(pi1.dx, pi1.dy);
      }
      if (!noneBorderBottom) {
        strokePath.arcToPoint(pi0, radius: Radius.circular(drawInnerR));
      }

      theCanvas?.drawPath(strokePath, strokePaint);
    }

    theCanvas?.restore();
  }

  // 绘制文本
  void drawText(
      {required double drawR,
      required String txt,
      required double angle,
      required int index,
      required double cellSize,
      double offset = 0,
      double fontSize = 10,
      Color color = Colors.black,
      bool vertical = false}) {
    double txtContainerWidth;
    double height;
    double offsetY;
    double layoutMaxWidth;
    if (vertical) {
      height = 1;
      drawR += ((txt.length) * fontSize - cellSize) / 2;
      txtContainerWidth = fontSize * 1;
      offsetY = 0;
      layoutMaxWidth = 0;
    } else {
      height = cellSize / fontSize;
      txtContainerWidth = fontSize * txt.length;
      offsetY = -(cellSize - 4 - fontSize) / 4;
      layoutMaxWidth = txtContainerWidth;
    }
    double radians = degreesToRadians(angle) * (index + offset);
    theCanvas
      ?..save()
      ..translate(_centerOffset.dx, _centerOffset.dy)
      ..rotate(radians)
      ..translate(-(txtContainerWidth / 2), offsetY);
    if (theCanvas != null) {
      TextPainter textPainter = TextPainter(
        text: TextSpan(
            text: txt,
            style: TextStyle(color: color, fontSize: fontSize, height: height)),
        textDirection: TextDirection.ltr,
        textAlign: TextAlign.center,
        textWidthBasis: TextWidthBasis.longestLine,
        maxLines: txt.length,
      )..layout(minWidth: layoutMaxWidth, maxWidth: layoutMaxWidth);

      textPainter.paint(theCanvas!, Offset(0, -drawR));
    }

    theCanvas?.restore();
  }

  // 绘制刻度线
  void drawScaleMarkLine(
      {required Paint paint, required double length, required double rotate}) {
    final outLoopR = _drawingBoardR;
    final innerLoopR = outLoopR - length;
    final theCorrectedAngle = degreesToRadians(rotate);
    theCanvas
      ?..save()
      ..translate(_centerOffset.dx, _centerOffset.dy)
      ..rotate(theCorrectedAngle)
      ..drawLine(Offset(0, -outLoopR), Offset(0, -innerLoopR), paint)
      ..restore();
  }

  // 绘制环形文字并自动统计使用的半径
  void drawCircleTextAndAutoUsedRadius({
    required List<CircleTxt> txtList,
    required double cellSize,
    double fontSize = 10,
    bool discreteRendering = false,
    bool border = false,
    Color? borderColor,
    bool fill = false,
    Color? fillColor,
    double space = 3,
    bool vertical = false,
    double offset = 0,
    bool noneBorderLeft = false,
    bool noneBorderRight = false,
    bool noneBorderTop = false,
    bool noneBorderBottom = false,
  }) {
    double usedRadius = drawCircleText(
        txtList: txtList,
        cellSize: cellSize,
        fontSize: fontSize,
        startDrawR: startRadius,
        startFromInner: true,
        discreteRendering: discreteRendering,
        border: border,
        borderColor: borderColor,
        fill: fill,
        fillColor: fillColor,
        space: space,
        vertical: vertical,
        offset: offset,
        noneBorderRight: noneBorderRight,
        noneBorderBottom: noneBorderBottom,
        noneBorderLeft: noneBorderLeft,
        noneBorderTop: noneBorderTop);
    setUsedRadius(usedRadius);
  }

  // 绘制文本
  double drawCircleText({
    required List<CircleTxt> txtList,
    required double cellSize,
    required double startDrawR,
    double fontSize = 10,
    bool startFromInner = false,
    bool discreteRendering = false,
    bool border = false,
    Color? borderColor,
    bool fill = false,
    Color? fillColor,
    double space = 3,
    double offset = 0,
    double angle = 360,
    bool vertical = false,
    bool noneBorderLeft = false,
    bool noneBorderRight = false,
    bool noneBorderTop = false,
    bool noneBorderBottom = false,
  }) {
    final double itemAngle = angle / txtList.length;
    double tempDrawInnerR, tempDrawOuterR, drawTxtR;

    if (startFromInner) {
      tempDrawInnerR = startDrawR;
      tempDrawOuterR = startDrawR + cellSize + 2 * space;
      drawTxtR = tempDrawOuterR - 1 * space;
    } else {
      tempDrawOuterR = startDrawR;
      tempDrawInnerR = startDrawR - cellSize - 2 * space;
      drawTxtR = tempDrawOuterR - 1 * space;
    }

    for (int i = 0; i < txtList.length; i++) {
      CircleTxt element = txtList[i];

      drawSector(
          drawOuterR: tempDrawOuterR,
          drawInnerR: tempDrawInnerR,
          index: i,
          border: element.border ?? border,
          borderColor:
              element.borderColor ?? borderColor ?? compassBorderDarkGray,
          angle: itemAngle,
          fill: element.fill ?? fill,
          fillColor: element.fillColor ?? fillColor,
          offset: offset,
          noneBorderTop: noneBorderTop,
          noneBorderRight: noneBorderRight,
          noneBorderBottom: noneBorderBottom,
          noneBorderLeft: noneBorderLeft);

      if (element.children != null) {
        final int len = element.children!.length;
        drawCircleText(
            txtList: element.children!,
            cellSize: cellSize,
            startDrawR: startDrawR,
            fontSize: fontSize,
            startFromInner: startFromInner,
            discreteRendering: discreteRendering,
            borderColor: borderColor,
            fill: false,
            fillColor: fillColor,
            space: space,
            offset: i * len - (len - 1) / 2,
            angle: itemAngle,
            border: false,
            vertical: vertical);
        continue;
      }

      if (discreteRendering && element.txt != null) {
        String txt = element.txt!;
        final len = txt.length;
        for (int j = 0; j < len; j++) {
          String charItem = txt[j];
          double charAngle = itemAngle / len;
          int tempIndex = i * len + j;
          drawText(
              drawR: drawTxtR,
              txt: charItem,
              cellSize: cellSize,
              fontSize: fontSize,
              color: element.color ?? Colors.black,
              index: tempIndex,
              offset: (i * len) - ((len - 1) / 2),
              angle: charAngle,
              vertical: vertical);
        }
      } else {
        drawText(
            drawR: drawTxtR,
            txt: element.txt ?? '',
            cellSize: cellSize,
            fontSize: fontSize,
            color: element.color ?? Colors.black,
            index: i,
            angle: itemAngle,
            offset: offset,
            vertical: vertical);
      }
    }

    return tempDrawOuterR - tempDrawInnerR;
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}
